package com.soft.base.spring.redis;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * class
 *
 * @author user
 * @date 2020/06/05
 */
public class RedisUtil {

    private static RedisTemplate redisTemplate;

    public static void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisUtil.redisTemplate = redisTemplate;
    }

    public static RedisTemplate client() {
        return redisTemplate;
    }

    public interface RememberCallback<T> {
        /**
         * get
         *
         * @return
         */
        T get();
    }

    public static <T> T remember(String key, long seconds, RememberCallback<T> callback) {
        Object object = client().opsForValue().get(key);
        if (null != object) {
            try {
                return (T) object;
            } catch (Exception e) {
            }
        }
        T value = callback.get();
        client().opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
        return value;
    }

    public static void delete(String key) {
        client().delete(key);
    }

    public static boolean exists(String key) {
        return client().hasKey(key);
    }

    public static void expire(String key, long seconds) {
        client().expire(key, seconds, TimeUnit.SECONDS);
    }

    public static Set keys(String key) {
        return client().keys(key);
    }

    public static Iterator<String> keysIterator(String key) {
        return (Iterator<String>) keys(key).iterator();
    }

    public static Boolean setnx(String key, String value) {
        return client().opsForValue().setIfAbsent(key, value);
    }

    public static Boolean setnx(String key, String value, long seconds) {
        return client().opsForValue().setIfAbsent(key, value, seconds, TimeUnit.SECONDS);
    }

    public static Boolean setnx(String key, Long value) {
        return setnx(key, value.toString());
    }

    public static Boolean setnx(String key, Long value, long seconds) {
        return client().opsForValue().setIfAbsent(key, value.toString(), seconds, TimeUnit.SECONDS);
    }

    public static void set(String key, String value) {
        client().opsForValue().set(key, value);
    }

    public static void set(String key, String value, long expire) {
        client().opsForValue().set(key, value, expire, TimeUnit.SECONDS);
    }

    public static void set(String key, Integer value) {
        client().opsForValue().set(key, value.toString());
    }

    public static void set(String key, Integer value, long expire) {
        client().opsForValue().set(key, value.toString(), expire, TimeUnit.SECONDS);
    }

    public static void set(String key, Long value) {
        client().opsForValue().set(key, value.toString());
    }

    public static void set(String key, Long value, long expire) {
        client().opsForValue().set(key, value.toString(), expire, TimeUnit.SECONDS);
    }

    public static void inc(String key) {
        client().opsForValue().increment(key, 1);
    }

    public static <T> void setObject(String key, T obj) {
        client().opsForValue().set(key, obj);
    }

    public static <T> void setObject(String key, T obj, long expire) {
        client().opsForValue().set(key, obj, expire, TimeUnit.SECONDS);
    }

    public static Integer getInteger(String key) {
        try {
            return Integer.valueOf((String) client().opsForValue().get(key));
        } catch (NumberFormatException e) {
        }
        return null;
    }

    public static Long getLong(String key) {
        try {
            return Long.valueOf((String) client().opsForValue().get(key));
        } catch (NumberFormatException e) {
        }
        return null;
    }

    public static Long getLong(String key, Long defaultValue) {
        try {
            Long v = Long.valueOf((String) client().opsForValue().get(key));
            if (null == v) {
                return defaultValue;
            }
            return v;
        } catch (NumberFormatException e) {
        }
        return defaultValue;
    }

    public static String getString(String key) {
        try {
            return (String) client().opsForValue().get(key);
        } catch (Exception e) {
        }
        return null;
    }

    public static <T> T getObject(String key, Class<T> cls) {
        try {
            return (T) client().opsForValue().get(key);
        } catch (Exception e) {
        }
        return null;
    }

    public static void hset(String key, String hkey, Long value) {
        client().opsForHash().put(key, hkey, value.toString());
    }

    public static void hset(String key, String hkey, String value) {
        client().opsForHash().put(key, hkey, value);
    }

    public static String hgetString(String key, String hkey) {
        try {
            return (String) client().opsForHash().get(key, hkey);
        } catch (Exception e) {
        }
        return null;
    }

    public static Long hgetLong(String key, String hkey) {
        try {
            return Long.valueOf((String) client().opsForHash().get(key, hkey));
        } catch (NumberFormatException e) {
        }
        return null;
    }

    public static List<Long> hgetLongs(String key, List<String> hkeys) {
        List list = client().opsForHash().multiGet(key, hkeys);
        ArrayList<Long> values = new ArrayList<>();
        for (Object o : list) {
            try {
                if (o instanceof String) {
                    values.add(Long.valueOf((String) o));
                }
            } catch (Exception e) {
                values.add(null);
            }
        }
        return values;
    }

    public static Boolean hhas(String key, String hkey) {
        return client().opsForHash().hasKey(key, hkey);
    }

    public static void hdel(String key, String hkey) {
        client().opsForHash().delete(key, hkey);
    }

    public static List<String> hkeys(String key) {
        Set keys = client().opsForHash().keys(key);
        return new ArrayList<>(keys);
    }

    public static long hsize(String key) {
        return client().opsForHash().size(key);
    }

    public static Long size(String key) {
        try {
            return client().opsForList().size(key);
        } catch (Exception e) {
        }
        return 0L;
    }

    public static void zAdd(String key, Object value, double score) {
        client().opsForZSet().add(key, value, score);
    }

    public static void zRemoveRangeByScore(String key, double min, double max) {
        client().opsForZSet().removeRangeByScore(key, min, max);
    }

    public static <T> List<T> zRangeByScore(String key, double min, double max, Class<T> cls) {
        Set<T> set = (Set<T>) client().opsForZSet().rangeByScore(key, min, max);
        return new ArrayList<>(set);
    }

    public static void zRemove(String key, Object obj) {
        client().opsForZSet().remove(key, obj);
    }

    public static long zcard(String key) {
        return client().opsForZSet().zCard(key);
    }

    public static void leftPush(String key, Object value) {
        client().boundListOps(key).leftPush(value);
    }

    public static Object leftPop(String key) {
        return client().boundListOps(key).leftPop();
    }

    public static void rightPush(String key, Object value) {
        client().boundListOps(key).rightPush(value);
    }

    public static Object rightPop(String key) {
        return client().boundListOps(key).rightPop();
    }

    /**
     * @param key key
     * @return
     * @Description: 获取自增长值
     */
    public static Long getIncr(String key) {
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
        return entityIdCounter.getAndIncrement();
    }

    /**
     * @param key   key
     * @param value 当前值
     * @Description: 初始化自增长值
     */
    public static void setIncr(String key, Long value) {
        RedisAtomicLong counter = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
        counter.set(value);
    }
}
